Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / data / src / commonMain / kotlin / org / meshtastic / core / data / manager / MeshDataHandlerImpl.kt

Displaying Raw • Download

core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerImpl.kt 2d20cd8a4708e2ef66e98d5259f3a97ec93f240a (2d20cd8a) Text, 29.99 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.data.manager

Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787co.touchlab.kermit.Severity
Tff7b72import T7ee787kotlinx.coroutines.CoroutineScope
Tff7b72import T7ee787kotlinx.coroutines.Job
Tff7b72import T7ee787kotlinx.coroutines.flow.first
Tff7b72import T7ee787kotlinx.coroutines.flow.firstOrNull
Tff7b72import T7ee787okio.ByteString
Tff7b72import T7ee787okio.ByteString.Companion.toByteString
Tff7b72import T7ee787org.koin.core.annotation.Named
Tff7b72import T7ee787org.koin.core.annotation.Single
Tff7b72import T7ee787org.meshtastic.core.common.util.nowMillis
Tff7b72import T7ee787org.meshtastic.core.common.util.nowSeconds
Tff7b72import T7ee787org.meshtastic.core.model.DataPacket
Tff7b72import T7ee787org.meshtastic.core.model.MeshBeaconOffer
Tff7b72import T7ee787org.meshtastic.core.model.MessageStatus
Tff7b72import T7ee787org.meshtastic.core.model.Node
Tff7b72import T7ee787org.meshtastic.core.model.NodeAddress
Tff7b72import T7ee787org.meshtastic.core.model.Reaction
Tff7b72import T7ee787org.meshtastic.core.model.destination
Tff7b72import T7ee787org.meshtastic.core.model.geofence.activeWaypointPackets
Tff7b72import T7ee787org.meshtastic.core.model.isBroadcast
Tff7b72import T7ee787org.meshtastic.core.model.isFromLocal
Tff7b72import T7ee787org.meshtastic.core.model.isModifiableBy
Tff7b72import T7ee787org.meshtastic.core.model.source
Tff7b72import T7ee787org.meshtastic.core.model.textMentionsNode
Tff7b72import T7ee787org.meshtastic.core.model.util.MeshDataMapper
Tff7b72import T7ee787org.meshtastic.core.model.util.decodeOrNull
Tff7b72import T7ee787org.meshtastic.core.model.util.isValidCodePoint
Tff7b72import T7ee787org.meshtastic.core.model.util.snrOrNull
Tff7b72import T7ee787org.meshtastic.core.model.util.toOneLiner
Tff7b72import T7ee787org.meshtastic.core.repository.AdminPacketHandler
Tff7b72import T7ee787org.meshtastic.core.repository.DataPair
Tff7b72import T7ee787org.meshtastic.core.repository.DiscoveryPacketCollectorRegistry
Tff7b72import T7ee787org.meshtastic.core.repository.MeshBeaconRepository
Tff7b72import T7ee787org.meshtastic.core.repository.MeshDataHandler
Tff7b72import T7ee787org.meshtastic.core.repository.MeshNotificationManager
Tff7b72import T7ee787org.meshtastic.core.repository.MessageFilter
Tff7b72import T7ee787org.meshtastic.core.repository.NeighborInfoHandler
Tff7b72import T7ee787org.meshtastic.core.repository.NodeManager
Tff7b72import T7ee787org.meshtastic.core.repository.Notification
Tff7b72import T7ee787org.meshtastic.core.repository.NotificationManager
Tff7b72import T7ee787org.meshtastic.core.repository.PacketHandler
Tff7b72import T7ee787org.meshtastic.core.repository.PacketRepository
Tff7b72import T7ee787org.meshtastic.core.repository.PlatformAnalytics
Tff7b72import T7ee787org.meshtastic.core.repository.RadioConfigRepository
Tff7b72import T7ee787org.meshtastic.core.repository.RadioInterfaceService
Tff7b72import T7ee787org.meshtastic.core.repository.RadioSessionContext
Tff7b72import T7ee787org.meshtastic.core.repository.ServiceStateWriter
Tff7b72import T7ee787org.meshtastic.core.repository.StoreForwardPacketHandler
Tff7b72import T7ee787org.meshtastic.core.repository.TelemetryPacketHandler
Tff7b72import T7ee787org.meshtastic.core.repository.TracerouteHandler
Tff7b72import T7ee787org.meshtastic.core.resources.Res
Tff7b72import T7ee787org.meshtastic.core.resources.critical_alert
Tff7b72import T7ee787org.meshtastic.core.resources.error_duty_cycle
Tff7b72import T7ee787org.meshtastic.core.resources.getStringSuspend
Tff7b72import T7ee787org.meshtastic.core.resources.mesh_beacon_notification_body
Tff7b72import T7ee787org.meshtastic.core.resources.mesh_beacon_notification_title
Tff7b72import T7ee787org.meshtastic.core.resources.unknown_username
Tff7b72import T7ee787org.meshtastic.core.resources.waypoint_received
Tff7b72import T7ee787org.meshtastic.proto.MeshBeacon
Tff7b72import T7ee787org.meshtastic.proto.MeshPacket
Tff7b72import T7ee787org.meshtastic.proto.Paxcount
Tff7b72import T7ee787org.meshtastic.proto.PortNum
Tff7b72import T7ee787org.meshtastic.proto.Position
Tff7b72import T7ee787org.meshtastic.proto.Routing
Tff7b72import T7ee787org.meshtastic.proto.StatusMessage
Tff7b72import T7ee787org.meshtastic.proto.User
Tff7b72import T7ee787org.meshtastic.proto.Waypoint

T8b949e/**
* Implementation of [MeshDataHandler] that decodes and routes incoming mesh data packets.
*
* This class handles the complexity of:
* 1. Mapping raw [MeshPacket] objects to domain-friendly [DataPacket] objects.
* 2. Routing packets to specialized handlers (e.g., Traceroute, NeighborInfo, Telemetry, Admin, SFPP).
* 3. Managing message history and persistence.
* 4. Triggering notifications for various packet types (Text, Waypoints).
*/
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffLongParameterListTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffTooManyFunctionsTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffCyclomaticComplexMethodTa5d6ff"Tb4b4b4)
Tf0883e@Single
Tff7b72class T56d364MeshDataHandlerImplTb4b4b4(
Tff7b72private Tff7b72val Te6edf3nodeManagerTb4b4b4: Te6edf3NodeManagerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3packetHandlerTb4b4b4: Te6edf3PacketHandlerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3serviceStateWriterTb4b4b4: Te6edf3ServiceStateWriterTb4b4b4,
Tff7b72private Tff7b72val Te6edf3packetRepositoryTb4b4b4: Te6edf3LazyTff7b72<Te6edf3PacketRepositoryTff7b72>Tb4b4b4,
Tff7b72private Tff7b72val Te6edf3notificationManagerTb4b4b4: Te6edf3NotificationManagerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3serviceNotificationsTb4b4b4: Te6edf3MeshNotificationManagerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3analyticsTb4b4b4: Te6edf3PlatformAnalyticsTb4b4b4,
Tff7b72private Tff7b72val Te6edf3dataMapperTb4b4b4: Te6edf3MeshDataMapperTb4b4b4,
Tff7b72private Tff7b72val Te6edf3tracerouteHandlerTb4b4b4: Te6edf3TracerouteHandlerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3neighborInfoHandlerTb4b4b4: Te6edf3NeighborInfoHandlerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3radioConfigRepositoryTb4b4b4: Te6edf3RadioConfigRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3messageFilterTb4b4b4: Te6edf3MessageFilterTb4b4b4,
Tff7b72private Tff7b72val Te6edf3storeForwardHandlerTb4b4b4: Te6edf3StoreForwardPacketHandlerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3telemetryHandlerTb4b4b4: Te6edf3TelemetryPacketHandlerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3adminPacketHandlerTb4b4b4: Te6edf3AdminPacketHandlerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3collectorRegistryTb4b4b4: Te6edf3DiscoveryPacketCollectorRegistryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3geofenceMonitorTb4b4b4: Te6edf3GeofenceMonitorTb4b4b4,
Tff7b72private Tff7b72val Te6edf3meshBeaconRepositoryTb4b4b4: Te6edf3MeshBeaconRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3radioInterfaceServiceTb4b4b4: Te6edf3RadioInterfaceServiceTb4b4b4,
Tf0883e@NamedTb4b4b4(Ta5d6ff"Ta5d6ffServiceScopeTa5d6ff"Tb4b4b4) Tff7b72private Tff7b72val Te6edf3scopeTb4b4b4: Te6edf3CoroutineScopeTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3MeshDataHandler Tb4b4b4{

Tff7b72private Tff7b72val Te6edf3rememberDataType Tff7b72=
Te6edf3setOfTb4b4b4(
Te6edf3PortNumTb4b4b4.Te6edf3TEXT_MESSAGE_APPTb4b4b4.Te6edf3valueTb4b4b4,
Te6edf3PortNumTb4b4b4.Te6edf3ALERT_APPTb4b4b4.Te6edf3valueTb4b4b4,
Te6edf3PortNumTb4b4b4.Te6edf3WAYPOINT_APPTb4b4b4.Te6edf3valueTb4b4b4,
Te6edf3PortNumTb4b4b4.Te6edf3NODE_STATUS_APPTb4b4b4.Te6edf3valueTb4b4b4,
Tb4b4b4)

Tff7b72override Tff7b72fun Td2a8ffhandleReceivedDataTb4b4b4(
Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4,
Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4,
Te6edf3logUuidTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3logInsertJobTb4b4b4: Te6edf3Job?Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3dataPacket Tff7b72= Te6edf3dataMapperTb4b4b4.Te6edf3toDataPacketTb4b4b4(Te6edf3packetTb4b4b4) Tff7b72?: Tff7b72return
Tff7b72val Te6edf3fromUs Tff7b72= Te6edf3myNodeNum Tff7b72=Tff7b72= Te6edf3packetTb4b4b4.Te6edf3from
Te6edf3dataPacketTb4b4b4.Te6edf3status Tff7b72= Te6edf3MessageStatusTb4b4b4.Te6edf3RECEIVED

Te6edf3handleDataPacketTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3fromUsTb4b4b4, Te6edf3sessionTb4b4b4, Te6edf3logUuidTb4b4b4, Te6edf3logInsertJobTb4b4b4)

Te6edf3analyticsTb4b4b4.Te6edf3trackTb4b4b4(Ta5d6ff"Ta5d6ffnum_data_receiveTa5d6ff"Tb4b4b4, Te6edf3DataPairTb4b4b4(Ta5d6ff"Ta5d6ffnum_data_receiveTa5d6ff"Tb4b4b4, T79c0ff1Tb4b4b4)Tb4b4b4)

T8b949e// Forward to discovery scan collector if active
Te6edf3collectorRegistryTb4b4b4.Te6edf3collectorTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3collector Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Te6edf3collectorTb4b4b4.Te6edf3isActiveTb4b4b4) Tb4b4b4{
Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3launchSessionWorkTb4b4b4(Te6edf3scopeTb4b4b4, Te6edf3sessionTb4b4b4) Tb4b4b4{
Te6edf3collectorTb4b4b4.Te6edf3onPacketReceivedTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3dataPacketTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhandleDataPacketTb4b4b4(
Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4,
Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4,
Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3fromUsTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4,
Te6edf3logUuidTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3logInsertJobTb4b4b4: Te6edf3Job?Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3decoded Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decoded Tff7b72?: Tff7b72return
Tff7b72when Tb4b4b4(Te6edf3decodedTb4b4b4.Te6edf3portnumTb4b4b4) Tb4b4b4{
Te6edf3PortNumTb4b4b4.Te6edf3TEXT_MESSAGE_APP Tff7b72-Tff7b72> Te6edf3handleTextMessageTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3sessionTb4b4b4)
Te6edf3PortNumTb4b4b4.Te6edf3NODE_STATUS_APP Tff7b72-Tff7b72> Te6edf3handleNodeStatusTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3sessionTb4b4b4)
Te6edf3PortNumTb4b4b4.Te6edf3ALERT_APP Tff7b72-Tff7b72> Te6edf3rememberDataPacketTb4b4b4(Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3session Tff7b72= Te6edf3sessionTb4b4b4)
Te6edf3PortNumTb4b4b4.Te6edf3WAYPOINT_APP Tff7b72-Tff7b72> Te6edf3handleWaypointTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3sessionTb4b4b4)
Te6edf3PortNumTb4b4b4.Te6edf3POSITION_APP Tff7b72-Tff7b72> Te6edf3handlePositionTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3sessionTb4b4b4)
Te6edf3PortNumTb4b4b4.Te6edf3NODEINFO_APP Tff7b72-Tff7b72> Tff7b72if Tb4b4b4(Tff7b72!Te6edf3fromUsTb4b4b4) Te6edf3handleNodeInfoTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3sessionTb4b4b4)
Te6edf3PortNumTb4b4b4.Te6edf3TELEMETRY_APP Tff7b72-Tff7b72> Te6edf3telemetryHandlerTb4b4b4.Te6edf3handleTelemetryTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3sessionTb4b4b4)
Tff7b72else Tff7b72-Tff7b72> Te6edf3handleSpecializedDataPacketTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3sessionTb4b4b4, Te6edf3logUuidTb4b4b4, Te6edf3logInsertJobTb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhandleSpecializedDataPacketTb4b4b4(
Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4,
Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4,
Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4,
Te6edf3logUuidTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3logInsertJobTb4b4b4: Te6edf3Job?Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3decoded Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decoded Tff7b72?: Tff7b72return
Tff7b72when Tb4b4b4(Te6edf3decodedTb4b4b4.Te6edf3portnumTb4b4b4) Tb4b4b4{
Te6edf3PortNumTb4b4b4.Te6edf3TRACEROUTE_APP Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3tracerouteHandlerTb4b4b4.Te6edf3handleTracerouteTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3logUuidTb4b4b4, Te6edf3logInsertJobTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4}

Te6edf3PortNumTb4b4b4.Te6edf3ROUTING_APP Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3handleRoutingTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3dataPacketTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4}

Te6edf3PortNumTb4b4b4.Te6edf3PAXCOUNTER_APP Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3handlePaxCounterTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4}

Te6edf3PortNumTb4b4b4.Te6edf3STORE_FORWARD_APP Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3storeForwardHandlerTb4b4b4.Te6edf3handleStoreAndForwardTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4}

Te6edf3PortNumTb4b4b4.Te6edf3STORE_FORWARD_PLUSPLUS_APP Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3storeForwardHandlerTb4b4b4.Te6edf3handleStoreForwardPlusPlusTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4}

Te6edf3PortNumTb4b4b4.Te6edf3ADMIN_APP Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3adminPacketHandlerTb4b4b4.Te6edf3handleAdminMessageTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4}

Te6edf3PortNumTb4b4b4.Te6edf3NEIGHBORINFO_APP Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3neighborInfoHandlerTb4b4b4.Te6edf3handleNeighborInfoTb4b4b4(Te6edf3packetTb4b4b4)
Tb4b4b4}

Te6edf3PortNumTb4b4b4.Te6edf3ATAK_PLUGINTb4b4b4,
Te6edf3PortNumTb4b4b4.Te6edf3ATAK_PLUGIN_V2Tb4b4b4,
Te6edf3PortNumTb4b4b4.Te6edf3PRIVATE_APPTb4b4b4,
Tff7b72-Tff7b72> Tb4b4b4{Tb4b4b4}

Te6edf3PortNumTb4b4b4.Te6edf3RANGE_TEST_APPTb4b4b4,
Te6edf3PortNumTb4b4b4.Te6edf3DETECTION_SENSOR_APPTb4b4b4,
Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3handleRangeTestTb4b4b4(Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4}

Te6edf3PortNumTb4b4b4.Te6edf3MESH_BEACON_APP Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3handleMeshBeaconTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4}

Tff7b72else Tff7b72-Tff7b72> Tb4b4b4{Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhandleRangeTestTb4b4b4(Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3u Tff7b72= Te6edf3dataPacketTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3dataType Tff7b72= Te6edf3PortNumTb4b4b4.Te6edf3TEXT_MESSAGE_APPTb4b4b4.Te6edf3valueTb4b4b4)
Te6edf3rememberDataPacketTb4b4b4(Te6edf3uTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3session Tff7b72= Te6edf3sessionTb4b4b4)
Tb4b4b4}

T8b949e/**
* A Mesh Beacon is an advisory, zero-hop invitation from another mesh — not a contact in the local NodeDB and not a
* message. We stash the offer in [MeshBeaconRepository] for the Discovery surface to present, and fire a single
* low-priority notification when the invitation is first seen (not on every periodic re-broadcast). Only beacons
* carrying a join offer (a channel) are actionable; message-only beacons are ignored.
*/
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffhandleMeshBeaconTb4b4b4(Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tb4b4b4{
T8b949e// Ignore our own beacons (spec FR-001) — once broadcast is enabled a node that also listens would self-notify.
Tff7b72if Tb4b4b4(Te6edf3packetTb4b4b4.Te6edf3from Tff7b72=Tff7b72= Te6edf3myNodeNumTb4b4b4) Tff7b72return
Tff7b72val Te6edf3payload Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decodedTff7b72?.Te6edf3payload Tff7b72?: Tff7b72return
Tff7b72val Te6edf3beacon Tff7b72= Te6edf3MeshBeaconTb4b4b4.Te6edf3ADAPTERTb4b4b4.Te6edf3decodeOrNullTb4b4b4(Te6edf3payloadTb4b4b4, Te6edf3LoggerTb4b4b4)
T8b949e// Only actionable beacons (carrying a channel offer) that we haven't already seen warrant a notification.
Tff7b72if Tb4b4b4(Te6edf3beaconTff7b72?.Te6edf3offer_channel Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tff7b72return
Tff7b72val Te6edf3offer Tff7b72=
Te6edf3MeshBeaconOfferTb4b4b4(
Te6edf3fromNodeNum Tff7b72= Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4,
Te6edf3beacon Tff7b72= Te6edf3beaconTb4b4b4,
T8b949e// [MeshBeaconOffer.snr] is not nullable, so absent narrows to 0f. See [snrOrNull].
Te6edf3snr Tff7b72= Te6edf3packetTb4b4b4.Te6edf3snrOrNullTb4b4b4(Tb4b4b4) Tff7b72?: T79c0ff0fTb4b4b4,
Te6edf3rssi Tff7b72= Te6edf3packetTb4b4b4.Te6edf3rx_rssiTb4b4b4,
Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3meshBeaconRepositoryTb4b4b4.Te6edf3addTb4b4b4(Te6edf3offerTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3launchSessionWorkTb4b4b4(Te6edf3scopeTb4b4b4, Te6edf3sessionTb4b4b4) Tb4b4b4{
Te6edf3notificationManagerTb4b4b4.Te6edf3dispatchTb4b4b4(
Te6edf3NotificationTb4b4b4(
Te6edf3title Tff7b72= Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3mesh_beacon_notification_titleTb4b4b4)Tb4b4b4,
Te6edf3message Tff7b72= Te6edf3offerTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3ifBlank Tb4b4b4{ Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3mesh_beacon_notification_bodyTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3category Tff7b72= Te6edf3NotificationTb4b4b4.Te6edf3CategoryTb4b4b4.Te6edf3MeshBeaconTb4b4b4,
T8b949e// Literal URI avoids a core:navigation module dep (see NodeManagerImpl).
Te6edf3deepLinkUri Tff7b72= Ta5d6ff"Ta5d6ffmeshtastic://meshtastic/discoveryTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhandlePaxCounterTb4b4b4(Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3payload Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decodedTff7b72?.Te6edf3payload Tff7b72?: Tff7b72return
Tff7b72val Te6edf3p Tff7b72= Te6edf3PaxcountTb4b4b4.Te6edf3ADAPTERTb4b4b4.Te6edf3decodeOrNullTb4b4b4(Te6edf3payloadTb4b4b4, Te6edf3LoggerTb4b4b4) Tff7b72?: Tff7b72return
Te6edf3nodeManagerTb4b4b4.Te6edf3handleReceivedPaxcounterTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4, Te6edf3pTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhandlePositionTb4b4b4(
Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4,
Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4,
Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3payload Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decodedTff7b72?.Te6edf3payload Tff7b72?: Tff7b72return
Tff7b72val Te6edf3p Tff7b72= Te6edf3PositionTb4b4b4.Te6edf3ADAPTERTb4b4b4.Te6edf3decodeOrNullTb4b4b4(Te6edf3payloadTb4b4b4, Te6edf3LoggerTb4b4b4) Tff7b72?: Tff7b72return
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffPosition from Tffd700${Te6edf3packetTb4b4b4.Te6edf3fromTffd700}Ta5d6ff: Tffd700${Te6edf3PositionTb4b4b4.Te6edf3ADAPTERTb4b4b4.Te6edf3toOneLinerTb4b4b4(Te6edf3pTb4b4b4)Tffd700}Ta5d6ff" Tb4b4b4}
Te6edf3nodeManagerTb4b4b4.Te6edf3handleReceivedPositionTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3pTb4b4b4, Te6edf3dataPacketTb4b4b4.Te6edf3timeTb4b4b4, Te6edf3sessionTb4b4b4)
Te6edf3geofenceMonitorTb4b4b4.Te6edf3onPositionReceivedTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3pTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhandleWaypointTb4b4b4(
Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4,
Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4,
Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3payload Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decodedTff7b72?.Te6edf3payload Tff7b72?: Tff7b72return
Tff7b72val Te6edf3u Tff7b72= Te6edf3WaypointTb4b4b4.Te6edf3ADAPTERTb4b4b4.Te6edf3decodeTb4b4b4(Te6edf3payloadTb4b4b4)
T8b949e// A locked waypoint may only be created/updated by its owner; drop it if the sender isn't allowed to modify it.
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3uTb4b4b4.Te6edf3isModifiableByTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4)Tb4b4b4) Tff7b72return
T8b949e// icon is an arbitrary int on the wire and a waypoint with expire == 0 is retained indefinitely, so
T8b949e// normalise an unrenderable code point here at the trust boundary rather than relying on every consumer to
T8b949e// guard (0 means "use the default pushpin").
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3uTb4b4b4.Te6edf3iconTb4b4b4.Te6edf3isValidCodePointTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffClearing an out-of-range waypoint icon code point (Tffd700${Te6edf3uTb4b4b4.Te6edf3iconTffd700}Ta5d6ff)Ta5d6ff" Tb4b4b4}
Te6edf3dataPacketTb4b4b4.Te6edf3bytes Tff7b72= Te6edf3WaypointTb4b4b4.Te6edf3ADAPTERTb4b4b4.Te6edf3encodeTb4b4b4(Te6edf3uTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3icon Tff7b72= T79c0ff0Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3toByteStringTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72val Te6edf3updateNotification Tff7b72= Te6edf3uTb4b4b4.Te6edf3expire Tff7b72> Te6edf3nowSecondsTb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)
Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3launchSessionWorkTb4b4b4(Te6edf3scopeTb4b4b4, Te6edf3sessionTb4b4b4) Tb4b4b4{
T8b949e// Persisted-owner enforcement: a stored, locked waypoint may only be modified by the node it is locked to.
T8b949e// The inbound check above only validates the incoming payload, so without this a non-owner could hijack a
T8b949e// stored locked waypoint by replaying its id with locked_to = 0 (unlock) or their own num (takeover). The
T8b949e// read and the write share this one lease so the check sees a committed snapshot (see [persistDataPacket]).
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3storedWaypointModifiableByTb4b4b4(Te6edf3uTb4b4b4.Te6edf3idTb4b4b4, Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4)Tb4b4b4) Tff7b72returnTf0883e@launchSessionWork
Te6edf3persistDataPacketTb4b4b4(Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3updateNotificationTb4b4b4)
Tb4b4b4}
Tb4b4b4}

T8b949e/**
* Whether an inbound waypoint update from [from] may modify the currently-stored waypoint with [waypointId]. A new
* waypoint (nothing stored) or an unlocked stored waypoint is always modifiable; a locked stored waypoint is
* modifiable only by the node it is locked to — this deliberately rejects inbound unlock (`locked_to = 0`) attempts
* from anyone else.
*
* [PacketRepository.getWaypoints] is a row-per-transmission firehose, so it is collapsed via
* [activeWaypointPackets] (newest-per-id, expired dropped) — the same normalisation the map UI and geofence engine
* use, so the three cannot drift. Waypoint packets are infrequent, so this one-shot read per waypoint is not hot.
*/
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffstoredWaypointModifiableByTb4b4b4(Te6edf3waypointIdTb4b4b4: Tffa657IntTb4b4b4, Te6edf3fromTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
T8b949e// firstOrNull().orEmpty(): getWaypoints() is a hot repository flow that always emits (an empty list when there
T8b949e// are none), but tolerate a flow that completes without emitting rather than throwing on the inbound path.
Tff7b72val Te6edf3active Tff7b72= Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3getWaypointsTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3firstOrNullTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3activeWaypointPacketsTb4b4b4(Te6edf3nowSecondsTb4b4b4)
Tff7b72val Te6edf3stored Tff7b72= Te6edf3activeTff7b72[Te6edf3waypointIdTff7b72]Tff7b72?.Te6edf3waypoint
Tff7b72return Te6edf3stored Tff7b72=Tff7b72= Tff7b72null Tff7b72|Tff7b72| Te6edf3storedTb4b4b4.Te6edf3isModifiableByTb4b4b4(Te6edf3fromTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhandleTextMessageTb4b4b4(
Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4,
Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4,
Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3decoded Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decoded Tff7b72?: Tff7b72return
Tff7b72if Tb4b4b4(Te6edf3decodedTb4b4b4.Te6edf3reply_id Tff7b72!Tff7b72= T79c0ff0 Tff7b72&Tff7b72& Te6edf3decodedTb4b4b4.Te6edf3emoji Tff7b72!Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3rememberReactionTb4b4b4(Te6edf3packetTb4b4b4, Te6edf3sessionTb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3rememberDataPacketTb4b4b4(Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3session Tff7b72= Te6edf3sessionTb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhandleNodeInfoTb4b4b4(Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3payload Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decodedTff7b72?.Te6edf3payload Tff7b72?: Tff7b72return
Tff7b72val Te6edf3u Tff7b72=
Te6edf3UserTb4b4b4.Te6edf3ADAPTERTb4b4b4.Te6edf3decodeTb4b4b4(Te6edf3payloadTb4b4b4)
Tb4b4b4.Te6edf3let Tb4b4b4{ Tff7b72if Tb4b4b4(Tffa657itTb4b4b4.Te6edf3is_licensed Tff7b72=Tff7b72= Tff7b72trueTb4b4b4) Tffa657itTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3public_key Tff7b72= Te6edf3ByteStringTb4b4b4.Te6edf3EMPTYTb4b4b4) Tff7b72else Tffa657it Tb4b4b4}
Tb4b4b4.Te6edf3let Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3packetTb4b4b4.Te6edf3via_mqtt Tff7b72=Tff7b72= Tff7b72true Tff7b72&Tff7b72& Tff7b72!Tffa657itTb4b4b4.Te6edf3long_nameTb4b4b4.Te6edf3endsWithTb4b4b4(Ta5d6ff"Ta5d6ff (MQTT)Ta5d6ff"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tffa657itTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3long_name Tff7b72= Ta5d6ff"Tffd700${Tffa657itTb4b4b4.Te6edf3long_nameTffd700}Ta5d6ff (MQTT)Ta5d6ff"Tb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Tffa657it
Tb4b4b4}
Tb4b4b4}
Te6edf3nodeManagerTb4b4b4.Te6edf3handleReceivedUserTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4, Te6edf3uTb4b4b4, Te6edf3packetTb4b4b4.Te6edf3channelTb4b4b4, Te6edf3session Tff7b72= Te6edf3sessionTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhandleNodeStatusTb4b4b4(
Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4,
Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4,
Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3payload Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decodedTff7b72?.Te6edf3payload Tff7b72?: Tff7b72return
Tff7b72val Te6edf3s Tff7b72= Te6edf3StatusMessageTb4b4b4.Te6edf3ADAPTERTb4b4b4.Te6edf3decodeOrNullTb4b4b4(Te6edf3payloadTb4b4b4, Te6edf3LoggerTb4b4b4) Tff7b72?: Tff7b72return
Te6edf3nodeManagerTb4b4b4.Te6edf3handleReceivedNodeStatusTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4, Te6edf3sTb4b4b4, Te6edf3sessionTb4b4b4)
Te6edf3rememberDataPacketTb4b4b4(Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3session Tff7b72= Te6edf3sessionTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhandleRoutingTb4b4b4(Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4, Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3payload Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decodedTff7b72?.Te6edf3payload Tff7b72?: Tff7b72return
Tff7b72val Te6edf3r Tff7b72= Te6edf3RoutingTb4b4b4.Te6edf3ADAPTERTb4b4b4.Te6edf3decodeOrNullTb4b4b4(Te6edf3payloadTb4b4b4, Te6edf3LoggerTb4b4b4) Tff7b72?: Tff7b72return
Tff7b72if Tb4b4b4(Te6edf3rTb4b4b4.Te6edf3error_reason Tff7b72=Tff7b72= Te6edf3RoutingTb4b4b4.Te6edf3ErrorTb4b4b4.Te6edf3DUTY_CYCLE_LIMITTb4b4b4) Tb4b4b4{
Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3launchSessionWorkTb4b4b4(Te6edf3scopeTb4b4b4, Te6edf3sessionTb4b4b4) Tb4b4b4{
Te6edf3serviceStateWriterTb4b4b4.Te6edf3setErrorMessageTb4b4b4(Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3error_duty_cycleTb4b4b4)Tb4b4b4, Te6edf3SeverityTb4b4b4.Te6edf3WarnTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Te6edf3handleAckNakTb4b4b4(
Te6edf3packetTb4b4b4.Te6edf3decodedTff7b72?.Te6edf3request_id Tff7b72?: T79c0ff0Tb4b4b4,
Te6edf3nodeManagerTb4b4b4.Te6edf3toNodeIDTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4)Tb4b4b4,
Te6edf3rTb4b4b4.Te6edf3error_reasonTff7b72?.Te6edf3value Tff7b72?: T79c0ff0Tb4b4b4,
Te6edf3dataPacketTb4b4b4.Te6edf3relayNodeTb4b4b4,
Te6edf3sessionTb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffCyclomaticComplexMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffhandleAckNakTb4b4b4(
Te6edf3requestIdTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3fromIdTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3routingErrorTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3relayNodeTb4b4b4: Tffa657Int?Tb4b4b4,
Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3launchSessionWorkTb4b4b4(Te6edf3scopeTb4b4b4, Te6edf3sessionTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3isAck Tff7b72= Te6edf3routingError Tff7b72=Tff7b72= Te6edf3RoutingTb4b4b4.Te6edf3ErrorTb4b4b4.Te6edf3NONETb4b4b4.Te6edf3value
Tff7b72val Te6edf3p Tff7b72= Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3getPacketByPacketIdTb4b4b4(Te6edf3requestIdTb4b4b4)
Tff7b72val Te6edf3reaction Tff7b72= Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3getReactionByPacketIdTb4b4b4(Te6edf3requestIdTb4b4b4)

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffMaxLineLengthTa5d6ff"Tb4b4b4)
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{
Tff7b72val Te6edf3statusInfo Tff7b72= Ta5d6ff"Ta5d6ffstatus=Tffd700${Te6edf3pTff7b72?.Te6edf3status Tff7b72?: Te6edf3reactionTff7b72?.Te6edf3statusTffd700}Ta5d6ff"
Ta5d6ff"Ta5d6ff[ackNak] req=Tffd700$Te6edf3requestIdTa5d6ff routeErr=Tffd700$Te6edf3routingErrorTa5d6ff isAck=Tffd700$Te6edf3isAckTa5d6ff Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6ffpacketId=Tffd700${Te6edf3pTff7b72?.Te6edf3id Tff7b72?: Te6edf3reactionTff7b72?.Te6edf3packetIdTffd700}Ta5d6ff dataId=Tffd700${Te6edf3pTff7b72?.Te6edf3idTffd700}Ta5d6ff Tffd700$Te6edf3statusInfoTa5d6ff"
Tb4b4b4}

Tff7b72val Te6edf3m Tff7b72=
Tff7b72when Tb4b4b4{
Te6edf3isAck Tff7b72&Tff7b72& Tb4b4b4(Te6edf3fromId Tff7b72=Tff7b72= Te6edf3pTff7b72?.Te6edf3to Tff7b72|Tff7b72| Te6edf3fromId Tff7b72=Tff7b72= Te6edf3reactionTff7b72?.Te6edf3toTb4b4b4) Tff7b72-Tff7b72> Te6edf3MessageStatusTb4b4b4.Te6edf3RECEIVED
Te6edf3isAck Tff7b72-Tff7b72> Te6edf3MessageStatusTb4b4b4.Te6edf3DELIVERED
Tff7b72else Tff7b72-Tff7b72> Te6edf3MessageStatusTb4b4b4.Te6edf3ERROR
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3p Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3pTb4b4b4.Te6edf3status Tff7b72!Tff7b72= Te6edf3MessageStatusTb4b4b4.Te6edf3RECEIVEDTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3updatedPacket Tff7b72=
Te6edf3pTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3status Tff7b72= Te6edf3mTb4b4b4, Te6edf3relays Tff7b72= Tff7b72if Tb4b4b4(Te6edf3isAckTb4b4b4) Te6edf3pTb4b4b4.Te6edf3relays Tff7b72+ T79c0ff1 Tff7b72else Te6edf3pTb4b4b4.Te6edf3relaysTb4b4b4, Te6edf3relayNode Tff7b72= Te6edf3relayNodeTb4b4b4)
Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3updateTb4b4b4(Te6edf3updatedPacketTb4b4b4, Te6edf3routingError Tff7b72= Te6edf3routingErrorTb4b4b4)
Tb4b4b4}

Te6edf3reactionTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3r Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Te6edf3rTb4b4b4.Te6edf3status Tff7b72!Tff7b72= Te6edf3MessageStatusTb4b4b4.Te6edf3RECEIVEDTb4b4b4) Tb4b4b4{
Tff7b72var Te6edf3updated Tff7b72= Te6edf3rTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3status Tff7b72= Te6edf3mTb4b4b4, Te6edf3routingError Tff7b72= Te6edf3routingErrorTb4b4b4, Te6edf3relayNode Tff7b72= Te6edf3relayNodeTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3isAckTb4b4b4) Tb4b4b4{
Te6edf3updated Tff7b72= Te6edf3updatedTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3relays Tff7b72= Te6edf3updatedTb4b4b4.Te6edf3relays Tff7b72+ T79c0ff1Tb4b4b4)
Tb4b4b4}
Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3updateReactionTb4b4b4(Te6edf3updatedTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Te6edf3packetHandlerTb4b4b4.Te6edf3removeResponseTb4b4b4(Te6edf3requestIdTb4b4b4, Te6edf3complete Tff7b72= Tff7b72trueTb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tff7b72override Tff7b72fun Td2a8ffrememberDataPacketTb4b4b4(
Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4,
Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3updateNotificationTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContext?Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3dataPacketTb4b4b4.Te6edf3dataType Tff7b72!in Te6edf3rememberDataTypeTb4b4b4) Tff7b72return
Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3launchSessionWorkTb4b4b4(Te6edf3scopeTb4b4b4, Te6edf3sessionTb4b4b4) Tb4b4b4{
Te6edf3persistDataPacketTb4b4b4(Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3updateNotificationTb4b4b4)
Tb4b4b4}
Tb4b4b4}

T8b949e/**
* Deduplicates, filters, persists, and (when appropriate) notifies for a single [dataPacket]. Runs inside a session
* lease — callers must launch it via [RadioInterfaceService.launchSessionWork] and must have already confirmed the
* packet's [DataPacket.dataType] is one of [rememberDataType]. Split out from [rememberDataPacket] so the waypoint
* path can gate persistence on a repository read within the same lease (see [handleWaypoint]).
*/
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffpersistDataPacketTb4b4b4(Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4: Tffa657IntTb4b4b4, Te6edf3updateNotificationTb4b4b4: Tffa657BooleanTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3fromLocal Tff7b72= Te6edf3dataPacketTb4b4b4.Te6edf3isFromLocalTb4b4b4(Te6edf3myNodeNumTb4b4b4)
Tff7b72val Te6edf3toBroadcast Tff7b72= Te6edf3dataPacketTb4b4b4.Te6edf3isBroadcast
Tff7b72val Te6edf3contactId Tff7b72= Tff7b72if Tb4b4b4(Te6edf3fromLocal Tff7b72|Tff7b72| Te6edf3toBroadcastTb4b4b4) Te6edf3dataPacketTb4b4b4.Te6edf3to Tff7b72else Te6edf3dataPacketTb4b4b4.Te6edf3from

T8b949e// contactKey: unique contact key filter (channel)+(nodeId)
Tff7b72val Te6edf3contactKey Tff7b72= Ta5d6ff"Tffd700${Te6edf3dataPacketTb4b4b4.Te6edf3channelTffd700}Tffd700$Te6edf3contactIdTa5d6ff"

Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3apply Tb4b4b4{
T8b949e// Check for duplicates before inserting
Tff7b72val Te6edf3existingPackets Tff7b72= Te6edf3findPacketsWithIdTb4b4b4(Te6edf3dataPacketTb4b4b4.Te6edf3idTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3existingPacketsTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{
Ta5d6ff"Ta5d6ffSkipping duplicate packet: packetId=Tffd700${Te6edf3dataPacketTb4b4b4.Te6edf3idTffd700}Ta5d6ff from=Tffd700${Te6edf3dataPacketTb4b4b4.Te6edf3fromTffd700}Ta5d6ff Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6ffto=Tffd700${Te6edf3dataPacketTb4b4b4.Te6edf3toTffd700}Ta5d6ff contactKey=Tffd700$Te6edf3contactKeyTa5d6ff" Tff7b72+
Ta5d6ff"Ta5d6ff (already have Tffd700${Te6edf3existingPacketsTb4b4b4.Te6edf3sizeTffd700}Ta5d6ff packet(s))Ta5d6ff"
Tb4b4b4}
Tff7b72return
Tb4b4b4}

T8b949e// Check if message should be filtered
Tff7b72val Te6edf3isFiltered Tff7b72= Te6edf3shouldFilterMessageTb4b4b4(Te6edf3dataPacketTb4b4b4, Te6edf3contactKeyTb4b4b4)

Te6edf3insertTb4b4b4(Te6edf3dataPacketTb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3contactKeyTb4b4b4, Te6edf3nowMillisTb4b4b4, Te6edf3read Tff7b72= Te6edf3fromLocal Tff7b72|Tff7b72| Te6edf3isFilteredTb4b4b4, Te6edf3filtered Tff7b72= Te6edf3isFilteredTb4b4b4)
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3isFilteredTb4b4b4) Tb4b4b4{
Te6edf3handlePacketNotificationTb4b4b4(Te6edf3dataPacketTb4b4b4, Te6edf3contactKeyTb4b4b4, Te6edf3updateNotificationTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72suspend Tff7b72fun Te6edf3PacketRepositoryTb4b4b4.Td2a8ffshouldFilterMessageTb4b4b4(Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4, Te6edf3contactKeyTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72val Te6edf3isIgnored Tff7b72= Te6edf3nodeManagerTb4b4b4.Te6edf3getNodeByIdTb4b4b4(Te6edf3dataPacketTb4b4b4.Te6edf3fromTb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)Tff7b72?.Te6edf3isIgnored Tff7b72=Tff7b72= Tff7b72true
Tff7b72if Tb4b4b4(Te6edf3isIgnoredTb4b4b4) Tff7b72return Tff7b72true

Tff7b72if Tb4b4b4(Te6edf3dataPacketTb4b4b4.Te6edf3dataType Tff7b72!Tff7b72= Te6edf3PortNumTb4b4b4.Te6edf3TEXT_MESSAGE_APPTb4b4b4.Te6edf3valueTb4b4b4) Tff7b72return Tff7b72false
Tff7b72val Te6edf3isFilteringDisabled Tff7b72= Te6edf3getContactSettingsTb4b4b4(Te6edf3contactKeyTb4b4b4)Tb4b4b4.Te6edf3filteringDisabled
Tff7b72return Te6edf3messageFilterTb4b4b4.Te6edf3shouldFilterTb4b4b4(Te6edf3dataPacketTb4b4b4.Te6edf3textTb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3isFilteringDisabledTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffhandlePacketNotificationTb4b4b4(
Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4,
Te6edf3contactKeyTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3updateNotificationTb4b4b4: Tffa657BooleanTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3conversationMuted Tff7b72= Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3getContactSettingsTb4b4b4(Te6edf3contactKeyTb4b4b4)Tb4b4b4.Te6edf3isMuted
Tff7b72val Te6edf3nodeMuted Tff7b72= Te6edf3nodeManagerTb4b4b4.Te6edf3getNodeByIdTb4b4b4(Te6edf3dataPacketTb4b4b4.Te6edf3fromTb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)Tff7b72?.Te6edf3isMuted Tff7b72=Tff7b72= Tff7b72true
T8b949e// A mention of our own id is a targeted ping, so it breaks through a muted channel/conversation
T8b949e// (per meshtastic/design#21). Node mute is a stronger, per-sender signal and stays authoritative:
T8b949e// a muted node cannot force a notification by spamming @-mentions.
Tff7b72val Te6edf3mentionsMe Tff7b72=
Te6edf3dataPacketTb4b4b4.Te6edf3dataType Tff7b72=Tff7b72= Te6edf3PortNumTb4b4b4.Te6edf3TEXT_MESSAGE_APPTb4b4b4.Te6edf3value Tff7b72&Tff7b72&
Te6edf3textMentionsNodeTb4b4b4(Te6edf3dataPacketTb4b4b4.Te6edf3textTb4b4b4, Te6edf3nodeManagerTb4b4b4.Te6edf3getMyIdTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3isSilent Tff7b72= Te6edf3nodeMuted Tff7b72|Tff7b72| Tb4b4b4(Te6edf3conversationMuted Tff7b72&Tff7b72& Tff7b72!Te6edf3mentionsMeTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3dataPacketTb4b4b4.Te6edf3dataType Tff7b72=Tff7b72= Te6edf3PortNumTb4b4b4.Te6edf3ALERT_APPTb4b4b4.Te6edf3value Tff7b72&Tff7b72& Tff7b72!Te6edf3isSilentTb4b4b4) Tb4b4b4{
Te6edf3notificationManagerTb4b4b4.Te6edf3dispatchTb4b4b4(
Te6edf3NotificationTb4b4b4(
Te6edf3title Tff7b72= Te6edf3getSenderNameTb4b4b4(Te6edf3dataPacketTb4b4b4)Tb4b4b4,
Te6edf3message Tff7b72= Te6edf3dataPacketTb4b4b4.Te6edf3alert Tff7b72?: Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3critical_alertTb4b4b4)Tb4b4b4,
Te6edf3category Tff7b72= Te6edf3NotificationTb4b4b4.Te6edf3CategoryTb4b4b4.Te6edf3AlertTb4b4b4,
Te6edf3contactKey Tff7b72= Te6edf3contactKeyTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Te6edf3updateNotification Tff7b72&Tff7b72& Tff7b72!Te6edf3isSilentTb4b4b4) Tb4b4b4{
Te6edf3updateNotificationTb4b4b4(Te6edf3contactKeyTb4b4b4, Te6edf3dataPacketTb4b4b4, Te6edf3isSilentTb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffgetSenderNameTb4b4b4(Te6edf3packetTb4b4b4: Te6edf3DataPacketTb4b4b4)Tb4b4b4: Tffa657String Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3packetTb4b4b4.Te6edf3source Tff7b72is Te6edf3NodeAddressTb4b4b4.Te6edf3LocalTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3myId Tff7b72= Te6edf3nodeManagerTb4b4b4.Te6edf3getMyIdTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3nodeManagerTb4b4b4.Te6edf3getNodeByIdTb4b4b4(Te6edf3myIdTb4b4b4)Tff7b72?.Te6edf3userTff7b72?.Te6edf3long_name Tff7b72?: Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3unknown_usernameTb4b4b4)
Tb4b4b4}
Tff7b72return Te6edf3nodeManagerTb4b4b4.Te6edf3getNodeByIdTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4.Te6edf3orEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)Tff7b72?.Te6edf3userTff7b72?.Te6edf3long_name
Tff7b72?: Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3unknown_usernameTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffupdateNotificationTb4b4b4(Te6edf3contactKeyTb4b4b4: Tffa657StringTb4b4b4, Te6edf3dataPacketTb4b4b4: Te6edf3DataPacketTb4b4b4, Te6edf3isSilentTb4b4b4: Tffa657BooleanTb4b4b4) Tb4b4b4{
Tff7b72when Tb4b4b4(Te6edf3dataPacketTb4b4b4.Te6edf3dataTypeTb4b4b4) Tb4b4b4{
Te6edf3PortNumTb4b4b4.Te6edf3TEXT_MESSAGE_APPTb4b4b4.Te6edf3value Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72val Te6edf3message Tff7b72= Te6edf3dataPacketTb4b4b4.Te6edf3textTff7b72!!
Tff7b72val Te6edf3isBroadcast Tff7b72= Te6edf3dataPacketTb4b4b4.Te6edf3destination Tff7b72is Te6edf3NodeAddressTb4b4b4.Te6edf3Broadcast
Tff7b72val Te6edf3channelName Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3isBroadcastTb4b4b4) Tb4b4b4{
Te6edf3radioConfigRepositoryTb4b4b4.Te6edf3channelSetFlowTb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3settingsTb4b4b4.Te6edf3getOrNullTb4b4b4(Te6edf3dataPacketTb4b4b4.Te6edf3channelTb4b4b4)Tff7b72?.Te6edf3name
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72null
Tb4b4b4}
Te6edf3serviceNotificationsTb4b4b4.Te6edf3updateMessageNotificationTb4b4b4(
Te6edf3contactKeyTb4b4b4,
Te6edf3getSenderNameTb4b4b4(Te6edf3dataPacketTb4b4b4)Tb4b4b4,
Te6edf3messageTb4b4b4,
Te6edf3isBroadcastTb4b4b4,
Te6edf3channelNameTb4b4b4,
Te6edf3isSilentTb4b4b4,
Tb4b4b4)
Tb4b4b4}

Te6edf3PortNumTb4b4b4.Te6edf3WAYPOINT_APPTb4b4b4.Te6edf3value Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72val Te6edf3message Tff7b72= Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3waypoint_receivedTb4b4b4, Te6edf3dataPacketTb4b4b4.Te6edf3waypointTff7b72!!Tb4b4b4.Te6edf3nameTb4b4b4)
Te6edf3notificationManagerTb4b4b4.Te6edf3dispatchTb4b4b4(
Te6edf3NotificationTb4b4b4(
Te6edf3title Tff7b72= Te6edf3getSenderNameTb4b4b4(Te6edf3dataPacketTb4b4b4)Tb4b4b4,
Te6edf3message Tff7b72= Te6edf3messageTb4b4b4,
Te6edf3category Tff7b72= Te6edf3NotificationTb4b4b4.Te6edf3CategoryTb4b4b4.Te6edf3MessageTb4b4b4,
Te6edf3contactKey Tff7b72= Te6edf3contactKeyTb4b4b4,
Te6edf3isSilent Tff7b72= Te6edf3isSilentTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tff7b72else Tff7b72-Tff7b72> Tff7b72return
Tb4b4b4}
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffKotlinConstantConditionsTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffrememberReactionTb4b4b4(Te6edf3packetTb4b4b4: Te6edf3MeshPacketTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tff7b72=
Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3launchSessionWorkTb4b4b4(Te6edf3scopeTb4b4b4, Te6edf3sessionTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3decoded Tff7b72= Te6edf3packetTb4b4b4.Te6edf3decoded Tff7b72?: Tff7b72returnTf0883e@launchSessionWork
Tff7b72val Te6edf3emoji Tff7b72= Te6edf3decodedTb4b4b4.Te6edf3payloadTb4b4b4.Te6edf3toByteArrayTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3decodeToStringTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3fromId Tff7b72= Te6edf3nodeManagerTb4b4b4.Te6edf3toNodeIDTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4)

Tff7b72val Te6edf3fromNode Tff7b72= Te6edf3nodeManagerTb4b4b4.Te6edf3nodeDBbyNodeNumTff7b72[Te6edf3packetTb4b4b4.Te6edf3fromTff7b72] Tff7b72?: Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= Te6edf3packetTb4b4b4.Te6edf3fromTb4b4b4)
Tff7b72val Te6edf3toNode Tff7b72= Te6edf3nodeManagerTb4b4b4.Te6edf3nodeDBbyNodeNumTff7b72[Te6edf3packetTb4b4b4.Te6edf3toTff7b72] Tff7b72?: Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= Te6edf3packetTb4b4b4.Te6edf3toTb4b4b4)

Tff7b72val Te6edf3reaction Tff7b72=
Te6edf3ReactionTb4b4b4(
Te6edf3replyId Tff7b72= Te6edf3decodedTb4b4b4.Te6edf3reply_idTb4b4b4,
Te6edf3user Tff7b72= Te6edf3fromNodeTb4b4b4.Te6edf3userTb4b4b4,
Te6edf3emoji Tff7b72= Te6edf3emojiTb4b4b4,
Te6edf3timestamp Tff7b72= Te6edf3nowMillisTb4b4b4,
T8b949e// [Reaction.snr] is not nullable, so absent narrows to 0f here. See [snrOrNull].
Te6edf3snr Tff7b72= Te6edf3packetTb4b4b4.Te6edf3snrOrNullTb4b4b4(Tb4b4b4) Tff7b72?: T79c0ff0fTb4b4b4,
Te6edf3rssi Tff7b72= Te6edf3packetTb4b4b4.Te6edf3rx_rssiTb4b4b4,
Te6edf3hopsAway Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3packetTb4b4b4.Te6edf3hop_start Tff7b72=Tff7b72= T79c0ff0 Tff7b72|Tff7b72| Te6edf3packetTb4b4b4.Te6edf3hop_limit Tff7b72> Te6edf3packetTb4b4b4.Te6edf3hop_startTb4b4b4) Tb4b4b4{
Te6edf3HOPS_AWAY_UNAVAILABLE
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3packetTb4b4b4.Te6edf3hop_start Tff7b72- Te6edf3packetTb4b4b4.Te6edf3hop_limit
Tb4b4b4}Tb4b4b4,
Te6edf3packetId Tff7b72= Te6edf3packetTb4b4b4.Te6edf3idTb4b4b4,
Te6edf3status Tff7b72= Te6edf3MessageStatusTb4b4b4.Te6edf3RECEIVEDTb4b4b4,
Te6edf3to Tff7b72= Te6edf3toNodeTb4b4b4.Te6edf3userTb4b4b4.Te6edf3idTb4b4b4,
Te6edf3channel Tff7b72= Te6edf3packetTb4b4b4.Te6edf3channelTb4b4b4,
Tb4b4b4)

T8b949e// Check for duplicates before inserting
Tff7b72val Te6edf3existingReactions Tff7b72= Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3findReactionsWithIdTb4b4b4(Te6edf3packetTb4b4b4.Te6edf3idTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3existingReactionsTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{
Ta5d6ff"Ta5d6ffSkipping duplicate reaction: packetId=Tffd700${Te6edf3packetTb4b4b4.Te6edf3idTffd700}Ta5d6ff replyId=Tffd700${Te6edf3decodedTb4b4b4.Te6edf3reply_idTffd700}Ta5d6ff Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6fffrom=Tffd700$Te6edf3fromIdTa5d6ff emoji=Tffd700$Te6edf3emojiTa5d6ff (already have Tffd700${Te6edf3existingReactionsTb4b4b4.Te6edf3sizeTffd700}Ta5d6ff reaction(s))Ta5d6ff"
Tb4b4b4}
Tff7b72returnTf0883e@launchSessionWork
Tb4b4b4}

Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3insertReactionTb4b4b4(Te6edf3reactionTb4b4b4, Te6edf3nodeManagerTb4b4b4.Te6edf3myNodeNumTb4b4b4.Te6edf3value Tff7b72?: T79c0ff0Tb4b4b4)

T8b949e// Find the original packet to get the contactKey
Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3getPacketByPacketIdTb4b4b4(Te6edf3decodedTb4b4b4.Te6edf3reply_idTb4b4b4)Tff7b72?.Te6edf3let Tb4b4b4{ Te6edf3originalPacket Tff7b72-Tff7b72>
T8b949e// Skip notification if the original message was filtered
Tff7b72val Te6edf3targetId Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3originalPacketTb4b4b4.Te6edf3source Tff7b72is Te6edf3NodeAddressTb4b4b4.Te6edf3LocalTb4b4b4) Te6edf3originalPacketTb4b4b4.Te6edf3to Tff7b72else Te6edf3originalPacketTb4b4b4.Te6edf3from
Tff7b72val Te6edf3contactKey Tff7b72= Ta5d6ff"Tffd700${Te6edf3originalPacketTb4b4b4.Te6edf3channelTffd700}Tffd700$Te6edf3targetIdTa5d6ff"
Tff7b72val Te6edf3conversationMuted Tff7b72= Te6edf3packetRepositoryTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3getContactSettingsTb4b4b4(Te6edf3contactKeyTb4b4b4)Tb4b4b4.Te6edf3isMuted
Tff7b72val Te6edf3nodeMuted Tff7b72= Te6edf3nodeManagerTb4b4b4.Te6edf3getNodeByIdTb4b4b4(Te6edf3fromIdTb4b4b4)Tff7b72?.Te6edf3isMuted Tff7b72=Tff7b72= Tff7b72true
Tff7b72val Te6edf3isSilent Tff7b72= Te6edf3conversationMuted Tff7b72|Tff7b72| Te6edf3nodeMuted

Tff7b72if Tb4b4b4(Tff7b72!Te6edf3isSilentTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3isBroadcast Tff7b72= Te6edf3originalPacketTb4b4b4.Te6edf3destination Tff7b72is Te6edf3NodeAddressTb4b4b4.Te6edf3Broadcast
Tff7b72val Te6edf3channelName Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3isBroadcastTb4b4b4) Tb4b4b4{
Te6edf3radioConfigRepositoryTb4b4b4.Te6edf3channelSetFlow
Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)
Tb4b4b4.Te6edf3settings
Tb4b4b4.Te6edf3getOrNullTb4b4b4(Te6edf3originalPacketTb4b4b4.Te6edf3channelTb4b4b4)
Tff7b72?.Te6edf3name
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72null
Tb4b4b4}
Te6edf3serviceNotificationsTb4b4b4.Te6edf3updateReactionNotificationTb4b4b4(
Te6edf3contactKeyTb4b4b4,
Te6edf3getSenderNameTb4b4b4(Te6edf3dataMapperTb4b4b4.Te6edf3toDataPacketTb4b4b4(Te6edf3packetTb4b4b4)Tff7b72!!Tb4b4b4)Tb4b4b4,
Te6edf3emojiTb4b4b4,
Te6edf3isBroadcastTb4b4b4,
Te6edf3channelNameTb4b4b4,
Te6edf3isSilentTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tff7b72companion Tff7b72object Tb4b4b4{
Tff7b72private Tff7b72const Tff7b72val Te6edf3HOPS_AWAY_UNAVAILABLE Tff7b72= Tff7b72-T79c0ff1
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.28s